package com.acme.superchat.service.server;

import com.acme.superchat.service.exception.NickExistsException;
import com.acme.superchat.service.history.HistoryRecord;

import java.sql.*;
import java.util.*;
import java.util.Date;

/**
 * Service to communicate between Clients
 * Implements infrastructural functions
 * @author Alexander Kotov (kotov.alex.22@gmail.com)
 */
public class SharedServer {
    private int increment = 0;
    private List<HistoryRecord> history = new LinkedList<>();
    private Map<String, ChatWorker> workers = new HashMap<>();
    private Map<String, Set<ChatWorker>> rooms = new HashMap<>();
    private Connection connection;

    public SharedServer(Connection connection) {
        this.connection = connection;
        rooms.put("default", new HashSet<ChatWorker>());
    }

    /**
     * Resgisters new client worker
     * @param chatWorker new client worker to register
     */
    public void addWorker(ChatWorker chatWorker) {
        while (workers.containsKey(new Integer(increment).toString())) {
            increment++;
        }
        chatWorker.setName(new Integer(increment).toString());
        this.workers.put(chatWorker.getName(), chatWorker);
        rooms.get("default").add(chatWorker);
        new Thread(chatWorker).start();
    }

    /**
     * Sends message to all clients
     * @param worker client - sender
     * @param message message to bcast
     */
    public void broadcast(ChatWorker worker, String message) {
        String room = findRoomByWorker(worker);
        HistoryRecord historyRecord = new HistoryRecord(new Date(), worker.getName(), message, room);
        this.persistHistory(historyRecord);
        for (ChatWorker chatWorker : rooms.get(room)) {
            chatWorker.sendMessage(historyRecord.toString());
        }
    }

    /**
     * Changes client id
     * @param worker client
     * @param name id
     * @throws NickExistsException
     */
    public void setName(ChatWorker worker, String name) throws NickExistsException {
        if (workers.containsKey(name)) {
            throw new NickExistsException(name);
        }
        workers.remove(worker.getName());
        worker.setName(name);
        workers.put(worker.getName(), worker);
    }

    /**
     * Returns list of all messages for current room
     * @param worker client
     * @return list of messages
     */
    public List<String> getHistory(ChatWorker worker) {
        List<String> result = new LinkedList<>();
        String room = findRoomByWorkerName(worker.getName());

        try {
            PreparedStatement selectQuery = connection.prepareStatement(
                    "SELECT * FROM MessageHistory WHERE room = ?"
            );
            selectQuery.setString(1,this.findRoomByWorker(worker));
            ResultSet resultSet = selectQuery.executeQuery();
            while (resultSet.next()){
                 result.add(new HistoryRecord(
                        resultSet.getTimestamp("date"),
                        resultSet.getString("nickname"),
                        resultSet.getString("message"),
                        resultSet.getString("room")
                ).toString());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        for (HistoryRecord historyRecord : history) {
            if (historyRecord.getRoom().equals(room)) {
                result.add(historyRecord.toString());
            }
        }
        return result;
    }

    /**
     * Changes client room  (will create on if one doesn't exists)
     * @param chatWorker client
     * @param room room name
     */
    public void changeRoom(ChatWorker chatWorker, String room) {
        String oldRoom = findRoomByWorker(chatWorker);
        rooms.get(oldRoom).remove(chatWorker);
        if (!rooms.containsKey(room)) {
            rooms.put(room, new HashSet<ChatWorker>());
        }
        rooms.get(room).add(chatWorker);
    }

    public String findRoomByWorker(ChatWorker worker) {
        for (String room : rooms.keySet()) {
            Set<ChatWorker> workersSet = rooms.get(room);
            for (ChatWorker curWorker : workersSet) {
                if (curWorker == worker) {
                    return room;
                }
            }
        }
        return null;
    }

    public String findRoomByWorkerName(String name) {
        for (String room : rooms.keySet()) {
            Set<ChatWorker> workersSet = rooms.get(room);
            for (ChatWorker curWorker : workersSet) {
                if (curWorker.getName().equals(name)) {
                    return room;
                }
            }
        }
        return null;
    }
    private void persistHistory(HistoryRecord historyRecord){
        PreparedStatement preparedStatement = null;
        try{
            preparedStatement = connection.prepareStatement(
                    "INSERT INTO MessageHistory(NICKNAME, ROOM, MESSAGE, DATE)" +
                            "VALUES" +
                            "(?, ?, ?, ?)"
            );

            preparedStatement.setString(1,historyRecord.getName());
            preparedStatement.setString(2,historyRecord.getRoom());
            preparedStatement.setString(3, historyRecord.getMessage());
            preparedStatement.setTimestamp(4, new Timestamp(historyRecord.getDate().getTime()));

            preparedStatement.execute();
        } catch (SQLException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

}
